﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using LEMMATIZERLib;
using GRAPHANLib;
using SYNANLib;
using System.Diagnostics;

namespace OffLineFilter
{
    public partial class MainForm : Form
    {
        public List<String> fileNames = new List<string>();
        public List<string> statisticsFileNames = new List<string>();
        public List<Bitmap> bitmaps = new List<Bitmap>();
        public List<List<List<Color>>> colorTexts = new List<List<List<Color>>>();
        public List<uint[]> shingleTexts = new List<uint[]>();
        public List<ResultItem> results = new List<ResultItem>();
        Dictionary dictionary;
        string solutionTreeFileName;

        public MainForm()
        {
            InitializeComponent();
        }

        private void btnOpenDictionary_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                return;

            dictionary = new Dictionary(openFileDialog.FileName);
            if (!dictionary.Load())
            {
                MessageBox.Show(String.Format("Не удалось загрузить файл словаря! {0}", dictionary.LastException),
                    "Ошибка",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                dictionary = null;
                return;
            }
            tsslDictionary.Text = String.Format("Загружен словарь: {0} лемм, {1:0.00} Мб", dictionary.Count, dictionary.LengthMb);
        }

        private void btnAddFile_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                return;

            string[] fileNameArr = openFileDialog.FileNames;
            foreach (String fileName in fileNameArr)
            {
                if (!File.Exists(fileName))
                {
                    MessageBox.Show("Невозможно открыть файл " + fileName + "!");
                    return;
                }
                if (fileNames.IndexOf(fileName) != -1)
                {
                    //MessageBox.Show("Выбранный файл уже был добавлен ранее");
                    return;
                }

                fileNames.Add(fileName);
                FileInfo fileInfo = new FileInfo(fileName);
                dataGridView.Rows.Add(fileName, fileInfo.Length / 1024);
            }
        }

        private void btnAnalyze_Click(object sender, EventArgs e)
        {
            if (dictionary == null)
            {
                MessageBox.Show("Необходимо загрузить словарь!");
                return;
            }

            if (String.IsNullOrWhiteSpace(solutionTreeFileName))
            {
                MessageBox.Show("Необходимо загрузить дерево решений!");
                return;
            }

            if (fileNames.Count == 0)
            {
                MessageBox.Show("Необходимо добавить файлы для анализа!");
                return;
            }

            tspbProgressBar.Value = 0;
            tspbProgressBar.Step = 100 / fileNames.Count;
            tspbProgressBar.Visible = true;

            SetButtonsEnabled(false);

            backgroundWorker.RunWorkerAsync(fileNames);
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<String> fileNames = e.Argument as List<String>;
            int step = 100 / fileNames.Count;
            for (int i = 0, progress = step; i < fileNames.Count; i++, progress += step)
            {
                Filter filter = new Filter(fileNames[i], dictionary, solutionTreeFileName, Convert.ToInt32(nudIdfThreshold.Value));
                IdfStageResult idfRes = filter.IdfStage();
                StatisticsStageResult statRes = filter.StatisticsStage();
                ColorsStageResult colorRes = filter.ColorsStage();
                uint[] shingles = filter.ShinglesStage(Convert.ToInt32(nudShingleLength.Value));

                ProgressState state = new ProgressState
                {
                    FileName = fileNames[i],
                    ItemIndex = i,
                    Result = idfRes.Result,
                    Defects = idfRes.Defects,
                    GzipRate = statRes.GzipRate,
                    Bz2Rate = statRes.Bz2Rate,
                    Variances = statRes.Variances,
                    Percents = statRes.Percents,
                    Solution = statRes.Solution,
                    AvgLength = statRes.AvgLength,
                    AvgLengthWithoutStop = statRes.AvgLengthWithoutStop,
                    Bitmap = colorRes.bitmap,
                    Squares = colorRes.squares,
                    Shingles = shingles
                };

                ResultItem result = new ResultItem()
                {
                    FileName = fileNames[i],
                    IdfSuccedeed = idfRes.Result,
                    Idf = idfRes.Defects,
                    StatisticsSuccedeed = statRes.Result,
                    Statistics = statRes.Solution,
                    ColorsSuccedeed = true,
                    ShinglesSuccedeed = true
                };
                results.Add(result);

                if (state.Result)
                    statisticsFileNames.Add(fileNames[i]);

                backgroundWorker.ReportProgress(progress, state);    
            }
            e.Result = true;
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            fileNames.Clear();
            dataGridView.Rows.Clear();
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            double[,] matches = new double[colorTexts.Count, colorTexts.Count];
            for (int i = 0; i < colorTexts.Count; i++)
            {
                for (int j = i + 1; j < colorTexts.Count; j++)
                {
                    ColorTextComparer comparer = new ColorTextComparer(colorTexts[i], colorTexts[j]);
                    double matching = comparer.Compare();
                    matches[i, j] = matching;
                    matches[j, i] = matching;
                }
                matches[i, i] = 1;
            }

            FillTable(dgvSimilarity, colorTexts.Count, matches);

            double[,] shingleMatches = new double[shingleTexts.Count, shingleTexts.Count];
            for (int i = 0; i < shingleTexts.Count; i++)
            {
                for (int j = i + 1; j < shingleTexts.Count; j++)
                {
                    double matching = ShinglesComparer.Compare(shingleTexts[i], shingleTexts[j]);
                    shingleMatches[i, j] = matching;
                    shingleMatches[j, i] = matching;
                }
                shingleMatches[i, i] = 1;
            }

            FillTable(dgvShingles, shingleTexts.Count, shingleMatches);

            double colorThreshold = (double)Convert.ToInt32(nudColorThreshold.Value) / (double)100;
            double shinglesThreshold = (double)Convert.ToInt32(nudShinglesThreshold.Value) / (double)100;

            for (int i = 1; i < colorTexts.Count; i++)
            {
                for (int j = 0; j < colorTexts.Count; j++)
                {
                    if (j != i)
                    {
                        if (matches[i, j] >= colorThreshold)
                        {
                            results[i].ColorsSuccedeed = false;
                            results[i].Colors = matches[i, j];
                            break;
                        }
                    }
                }
            }

            for (int i = 1; i < shingleTexts.Count; i++)
            {
                for (int j = 0; j < shingleTexts.Count; j++)
                {
                    if (j != i)
                    {
                        if (shingleMatches[i, j] >= shinglesThreshold)
                        {
                            results[i].ShinglesSuccedeed = false;
                            results[i].Shingles = shingleMatches[i, j];
                            break;
                        }
                    }
                }
            }

            dgvResults.AutoGenerateColumns = false;
            for (int i = 0; i < results.Count; i++)
            {
                dgvResults.Rows.Add(results[i].FileName,
                    results[i].Fails,
                    results[i].Idf,
                    results[i].Statistics,
                    results[i].Colors,
                    results[i].Shingles);
            }

            MessageBox.Show("Анализ файлов завершен!");
            tspbProgressBar.Visible = false;
        }

        private void FillTable(DataGridView dgv, int count, double[,] matches)
        {
            dgv.AutoGenerateColumns = false;
            for (int i = 0; i < count; i++)
                dgv.Columns.Add((i + 1).ToString(), (i + 1).ToString());

            for (int i = 0; i < count; i++)
            {
                String[] row = new String[count];
                for (int j = 0; j < count; j++)
                    row[j] = matches[i, j].ToString();
                dgv.Rows.Add(row);
            }
        }

        private void SetButtonsEnabled(bool enabled)
        {
            btnAddFile.Enabled = enabled;
            btnAnalyze.Enabled = enabled;
            btnClear.Enabled = enabled;
            btnOpenDictionary.Enabled = enabled;
            button1.Enabled = enabled;
        }

        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            tspbProgressBar.PerformStep();
            ProgressState state = (ProgressState)e.UserState;
            dataGridView.Rows[state.ItemIndex].Cells["AnalyzeResult"].Value = (state.Result) ? "ОК" : "Брак";
            dataGridView.Rows[state.ItemIndex].Cells["Defects"].Value = state.Defects.ToString();

            ProgressFlush(state);

            TreeNode fileNode = treeViewStatistics.Nodes.Add(state.FileName);
            fileNode.Nodes.Add("Степень сжатия GZip: " + state.GzipRate.ToString());
            fileNode.Nodes.Add("Степень сжатия Bz2: " + state.Bz2Rate.ToString());
            fileNode.Nodes.Add("Средняя длина предложения со СС: " + state.AvgLength.ToString());
            fileNode.Nodes.Add("Средняя длина предложения без СС: " + state.AvgLengthWithoutStop.ToString());
            TreeNode variances = fileNode.Nodes.Add("Дисперсии: ");
            variances.Nodes.AddRange(state.Variances.Select(pair => new TreeNode(pair.Key + ": " + pair.Value.ToString())).ToArray());
            TreeNode percents = fileNode.Nodes.Add("Проценты: ");
            percents.Nodes.AddRange(state.Percents.Select(pair => new TreeNode(pair.Key + ": " + pair.Value.ToString())).ToArray());
            TreeNode solution = fileNode.Nodes.Add("Решение: " + state.Solution);

            bitmaps.Add(state.Bitmap);
            colorTexts.Add(state.Squares);
            shingleTexts.Add(state.Shingles);
            lbBitmaps.Items.Add(state.FileName);
            listBox2.Items.Add(state.FileName);
        }

        private void ProgressFlush(ProgressState state)
        {
            StreamWriter file;
            if (!File.Exists("stat.txt"))
            {
                file = File.CreateText("stat.txt");
                String title = "Имя файла\tGzip\tBz2\tСр.дл.СС\tСр.дл.без СС\t";
                title = title + String.Join("\t", Statistics.Poses.Select(pos => "ДИСП " + pos));
                title = title + "\t" + String.Join("\t", Statistics.Poses.Select(pos => "ПРОЦ " + pos));
                file.WriteLine(title);
            }
            else
                file = new StreamWriter("stat.txt", append: true);

            String line = state.FileName + "\t" + state.GzipRate + "\t" + state.Bz2Rate + "\t" + state.AvgLength + "\t" + state.AvgLengthWithoutStop + "\t";
            line = line + String.Join("\t", Statistics.Poses.Select(pos =>
            {
                if (state.Variances.ContainsKey(pos))
                    return state.Variances[pos].ToString();
                return "0";
            }
            ));
            line = line + "\t" + String.Join("\t", Statistics.Poses.Select(pos =>
            {
                if (state.Percents.ContainsKey(pos))
                    return state.Percents[pos].ToString();
                return "0";
            }));
            file.WriteLine(line);
            file.Close();
        }

        private void btnIdfNext_Click(object sender, EventArgs e)
        {
            tabControl.SelectTab("tabStatistics");
        }

        private void btnAnalyzeStatistics_Click(object sender, EventArgs e)
        {
        }

        private void lbBitmaps_SelectedIndexChanged(object sender, EventArgs e)
        {
            pictureBox.Image = bitmaps[lbBitmaps.SelectedIndex];
        }

        private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            pictureBox2.Image = bitmaps[listBox2.SelectedIndex];
            ColorTextComparer comparer = new ColorTextComparer(colorTexts[lbBitmaps.SelectedIndex], colorTexts[listBox2.SelectedIndex]);
            pictureBox3.Image = comparer.GenerateBitmap();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                return;

            solutionTreeFileName = openFileDialog.FileName;
        }
    }

    public class ProgressState
    {
        public string FileName { get; set; }
        public int ItemIndex { get; set; }
        public bool Result { get; set; }
        public double Defects { get; set; }
        public double GzipRate { get; set; }
        public double Bz2Rate { get; set; }
        public Dictionary<String, double> Variances { get; set; }
        public Dictionary<String, double> Percents { get; set; }
        public String Solution { get; set; }
        public double AvgLength { get; set; }
        public double AvgLengthWithoutStop { get; set; }
        public Bitmap Bitmap { get; set; }
        public List<List<Color>> Squares { get; set; }
        public uint[] Shingles { get; set; }
    }

    public class ResultItem
    {
        public String FileName;
        public bool IdfSuccedeed;
        public double Idf;
        public bool StatisticsSuccedeed;
        public String Statistics;
        public bool ColorsSuccedeed;
        public double Colors;
        public bool ShinglesSuccedeed;
        public double Shingles;

        public int Fails
        {
            get
            {
                bool[] flags = new[] { IdfSuccedeed, StatisticsSuccedeed, ColorsSuccedeed, ShinglesSuccedeed };
                return flags.Where(f => !f).Count();
            }
        }
    }
}
